// dotnet $DOTNET_CSC_DLL -nologo -t:library -r:"../../Backend/System.dll" -r:"../../Backend/System.Collections.dll" -r:"../../The Scroll of Taiwu_Data/Managed/0Harmony.dll" -r:"../../Backend/mscorlib.dll" -r:"../../Backend/netstandard.dll" -r:"../../Backend/GameData.dll" -r:"../../Backend/Redzen.dll" -r:"../../The Scroll of Taiwu_Data/Managed/TaiwuModdingLib.dll" -r:"../../Backend/System.Private.CoreLib.dll" -r:"../../Backend/System.Runtime.dll" -r:"../../Backend/System.Linq.dll" -unsafe -optimize -deterministic Backend.cs *.CS -out:Backend.dll -debug
// -r:"../../Backend/System.IO.FileSystem.dll"

//! 编译方法，目前来说只能是自己改上面那行字，把缺的东西都补齐，之后送命令提示符或者用python脚本编译。
//! windows就是一坨shit……连通配符都不支持……不然我至少能写出类似 dotnet C:\Program Files\dotnet\sdk\*\Roslyn\bincore\csc.dll 这样的语法

// -r:"../../The Scroll of Taiwu_Data/Managed/Mono.Cecil.dll" -r:"../../The Scroll of Taiwu_Data/Managed/System.Core.dll"   -r:"../../The Scroll of Taiwu_Data/Managed/System.Composition.AttributedModel.dll" -r:"../../Backend/System.Runtime.dll"
/**
 *  Everyone's Unity Game Plugin
 *  Copyright (C) 2022 Neutron3529
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
//! 这是一个自带文档注释的Mod，虽然代码很shit，但里面有不少注释，或许会对之后准备玩过月逻辑的人有帮助。
//! 所有文档注释会用//!开头，请注意搜索
//! 首先是前后端，前端对后端的调用会先被塞进 ProcessMethodCall，之后分发给对应domain的CallMethod，由CallMethod进行进一步的分发。借助这一点，我们可以快速找到前端调用究竟是在使用哪个函数。
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using HarmonyLib;
using Utils;

namespace BetterSkipSkill;
[TaiwuModdingLib.Core.Plugin.PluginConfig("BetterSkipSkill","Neutron3529","0.1.1")]
public partial class BetterSkipSkill : TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin {
    static GameData.Utilities.RawDataPool AlwaysWinDataPool=new GameData.Utilities.RawDataPool(24);
    static List<GameData.GameDataBridge.Operation> WinMethodCallList=new List<GameData.GameDataBridge.Operation>();
    static GameData.GameDataBridge.OperationCollection win_ops=default (GameData.GameDataBridge.OperationCollection);
    // static GameData.GameDataBridge.OperationCollection _winCollection=default(GameData.GameDataBridge.OperationCollection);
    // class Test{
    //     [HarmonyPrefix, HarmonyPatch(typeof(GameData.Domains.Taiwu.Profession.ProfessionSkillHandle),"CivilianSkill_MakeCharacterLeaveSect")]
    //     public static void Prefix1(GameData.Domains.Character.Character character){
    //         logwarn($"{character.GetId()} : in {character.GetOrganizationInfo().OrgTemplateId},level {character.GetOrganizationInfo().Grade}");
    //     }
    //     [HarmonyPrefix, HarmonyPatch(typeof(GameData.GameDataBridge.GameDataBridge),"AdvanceFrame")]
    //     public static void Prefix2(){
    //         logger("Advance backend frame");
    //     }
    // }

    public override void Initialize(){
        that=this;
        this.HarmonyInstance = new RobustHarmonyInstance(this.GetGuid());

        int offset0=GameData.Serializer.Serializer.Serialize("WinState",AlwaysWinDataPool);
        GameData.Serializer.Serializer.Serialize(true,AlwaysWinDataPool);
        // int offset1=GameData.Serializer.Serializer.Serialize(string.Empty,AlwaysWinDataPool);
        // GameData.Serializer.Serializer.Serialize((int)0,AlwaysWinDataPool);

        // WinMethodCallList.Add(GameData.GameDataBridge.Operation.CreateMethodCall(-1,
            // GameData.Domains.DomainHelper.DomainName2DomainId["TaiwuEvent"],
            // GameData.Domains.TaiwuEvent.TaiwuEventDomainHelper.MethodName2MethodId["SetListenerBoolArg"],
        // 2,offset0));
        WinMethodCallList.Add(GameData.GameDataBridge.Operation.CreateMethodCall(-1,
            GameData.Domains.DomainHelper.DomainName2DomainId["TaiwuEvent"],
            GameData.Domains.TaiwuEvent.TaiwuEventDomainHelper.MethodName2MethodId["TriggerListener"],
        2,offset0));
        win_ops=new GameData.GameDataBridge.OperationCollection(WinMethodCallList, AlwaysWinDataPool);
    }
    public override void Dispose()=>this.HarmonyInstance.UnpatchSelf();
    public static void logger(string s)=>GameData.Utilities.AdaptableLog.Info(s);
    public static void logwarn(string s)=>GameData.Utilities.AdaptableLog.Warning("<color=#FFFF00>"+s+"</color>",true);
    public RobustHarmonyInstance HarmonyInstance;

    static bool qiecuo=false;
    static bool tiaozhan=false;
    static int ItemCount=0;
    static int ResourceCount=0;
    static sbyte[] RobGraveEventWeight=null;
    static BetterSkipSkill that=null;
    public static bool Enable(string key)=>that.enable(key);
    private bool enable(string key){
        try {
            return _enable(key);
        } catch(Exception ex){
            logwarn("enable 出错，出错键值为\""+key+"\"，错误原因是"+ex.Message);
            return false;
        }
    }
    private bool _enable(string key){
        bool enabled=false;
        if(key=="N03-qiecuo"){
            return GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref qiecuo) && qiecuo;
        } else if(key=="N03-tiaozhan"){
            return GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref tiaozhan) && tiaozhan;
        } else if(key=="N03-ItemCount"){
            return GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref ItemCount) && ItemCount>0;
        } else if(key=="N03-ResourceCount"){
            return GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref ResourceCount) && ResourceCount>0;
        }
        return GameData.Domains.DomainManager.Mod.GetSetting(this.ModIdStr, key, ref enabled) && enabled;
    }
    public override void OnModSettingUpdate(){
        this.HarmonyInstance.UnpatchSelf();
        // this.HarmonyInstance.PatchAll(typeof(Test));
        if(enable("NForceOFF"))return;
        if(enable("N06") || (enable("N01") && enable("N02")))this.HarmonyInstance.PatchAll(typeof(WinLifeSkillCombat));
        else if(enable("N01"))this.HarmonyInstance.PatchAll(typeof(StartLifeSkillCombat));
        if(enable("N07") || (enable("N01") && enable("N02")))this.HarmonyInstance.PatchAll(typeof(WinCricketCombat));
        else if(enable("N02"))this.HarmonyInstance.PatchAll(typeof(StartCricketCombat));
        if(enable("N03-qiecuo") | enable("N03-tiaozhan") | enable("N03-ItemCount") | enable("N03-ResourceCount"))this.HarmonyInstance.PatchAll(typeof(StartCombat));
        if(enable("N04")){
            RobGraveEventWeight=(sbyte[])(GlobalConfig.Instance.RobGraveEventWeight.Clone());
            GlobalConfig.Instance.RobGraveEventWeight[0]=0;
            GlobalConfig.Instance.RobGraveEventWeight[2]=127;
        } else if(RobGraveEventWeight!=null){
            GlobalConfig.Instance.RobGraveEventWeight=RobGraveEventWeight;
            RobGraveEventWeight=null;
        }
        if(enable("N05"))this.HarmonyInstance.PatchAll(typeof(SkipLogCharacterInteractDate));
        if(enable("N08"))this.HarmonyInstance.PatchAll(typeof(ExtraDomainFindTreasure));
        if(enable("N09"))this.HarmonyInstance.PatchAll(typeof(EventHelperSelectTaiwuTeammateForMatchmaking));
    }
    [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"StartLifeSkillCombat")]
    public class StartLifeSkillCombat {
        public static bool Prefix(int characterId, string onFinishEventId, GameData.Domains.TaiwuEvent.EventArgBox argBox){
            GameData.Domains.TaiwuEvent.EventHelper.EventHelper.StartCricketCombat(characterId, onFinishEventId, argBox);
            return false;
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"StartCricketCombat")]
    public class StartCricketCombat {
        public static bool Prefix(int characterId, string onFinishEventId, GameData.Domains.TaiwuEvent.EventArgBox argBox){
            GameData.Domains.TaiwuEvent.EventHelper.EventHelper.StartLifeSkillCombat(characterId, 16, onFinishEventId, argBox);
            return false;
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"StartLifeSkillCombat")]
    public class WinLifeSkillCombat {
        static FieldInfo ops=typeof(GameData.GameDataBridge.GameDataBridge).GetField("_operationCollections",(BindingFlags)(-1));
        static FieldInfo OperationCollectionsLock=typeof(GameData.GameDataBridge.GameDataBridge).GetField("OperationCollectionsLock",(BindingFlags)(-1));
        public static bool Prefix(int characterId, sbyte lifeSKillType, string onFinishEventId, GameData.Domains.TaiwuEvent.EventArgBox argBox) {
            // argBox.Set("WinState",true);
            GameData.Domains.DomainManager.TaiwuEvent.SetListener(onFinishEventId, argBox);
            var Lock=OperationCollectionsLock.GetValue(null);
            lock(Lock){
                ((List<GameData.GameDataBridge.OperationCollection>)ops.GetValue(null)).Add(win_ops);
            }
            return false;
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"StartCricketCombat")]
    public class WinCricketCombat {
        static FieldInfo ops=typeof(GameData.GameDataBridge.GameDataBridge).GetField("_operationCollections",(BindingFlags)(-1));
        static FieldInfo OperationCollectionsLock=typeof(GameData.GameDataBridge.GameDataBridge).GetField("OperationCollectionsLock",(BindingFlags)(-1));
        public static bool Prefix(int characterId, string onFinishEventId, GameData.Domains.TaiwuEvent.EventArgBox argBox){
            // argBox.Set("WinState",true);
            GameData.Domains.DomainManager.TaiwuEvent.SetListener(onFinishEventId, argBox);
            var Lock=OperationCollectionsLock.GetValue(null);
            lock(Lock){
                ((List<GameData.GameDataBridge.OperationCollection>)ops.GetValue(null)).Add(win_ops);
            }
            return false;
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"StartCombat")]
    public class StartCombat {
        public static void Prefix(ref short combatConfigId, string combatCompleteEvent, GameData.Domains.TaiwuEvent.EventArgBox argBox){
            // logger("StartCombat got "+combatConfigId);
            if(qiecuo && combatConfigId==0){combatConfigId=1;}else if(tiaozhan && combatConfigId==123){combatConfigId=2;}else if((ItemCount>0 || ResourceCount>0) && combatConfigId==122 && combatCompleteEvent=="93bca684-dfc9-4682-ae80-8108e129fcd2"){
                int graveId=-1;
                if(argBox.Get("TombId", ref graveId)){
                    var selfCharId=argBox.GetCharacter("RoleTaiwu").GetId();
                    // GameData.Domains.Item.ItemKey itemKey;
                    // for(var i=0;i<ItemCount && GameData.Domains.TaiwuEvent.EventHelper.EventHelper.ApplyRobItemFromGrave(selfCharId, graveId, out itemKey);i++){}
                    // sbyte b;
                    // int num;
                    // for(var i=0;i<ResourceCount && GameData.Domains.TaiwuEvent.EventHelper.EventHelper.ApplyRobResourceFromGrave(selfCharId, graveId, out b, out num);i++){}
                    {
                        GameData.Domains.Character.Grave grave;
                        if (GameData.Domains.DomainManager.Character.TryGetElement_Graves(graveId, out grave)) {
                            GameData.Domains.Character.Character selfChar;
                            if (GameData.Domains.DomainManager.Character.TryGetElement_Objects(selfCharId, out selfChar)) {
                                if(ItemCount>0){
                                    var graveInventory = grave.GetInventory();
                                    if(graveInventory.Items.Count>0){
                                        var lifeRecordCollection = GameData.Domains.DomainManager.LifeRecord.GetLifeRecordCollection();
                                        int currDate = GameData.Domains.DomainManager.World.GetCurrDate();
                                        var location = selfChar.GetLocation();
                                        var secretInformationCollection = GameData.Domains.DomainManager.Information.GetSecretInformationCollection();
                                        var i=0;
                                        foreach(var targetItemKey in (
                                            from x in graveInventory.Items.Keys
                                            orderby (uint)((GameData.Domains.Item.ItemTemplateHelper.GetGrade(x.ItemType, x.TemplateId)<<28)+(x.ItemType<<16)+x.TemplateId)
                                            select x
                                        )){
                                            if(i<ItemCount){
                                                // logger("got "+(uint)((GameData.Domains.Item.ItemTemplateHelper.GetGrade(targetItemKey.ItemType, targetItemKey.TemplateId)<<28)+(targetItemKey.ItemType<<16)+targetItemKey.TemplateId)+"："+GameData.Domains.Item.ItemTemplateHelper.GetName(targetItemKey.ItemType, targetItemKey.TemplateId));
                                                i++;
                                                int amount = graveInventory.Items[targetItemKey];
                                                graveInventory.OfflineRemove(targetItemKey, amount);
                                                selfChar.AddInventoryItem(GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext, targetItemKey, amount);

                                                lifeRecordCollection.AddRobItemFromGraveSucceed(selfCharId, currDate, graveId, location, targetItemKey.ItemType, targetItemKey.TemplateId);
                                                int secretInfoOffset = secretInformationCollection.AddRobGraveItem(selfCharId, graveId, (ulong)targetItemKey);
                                                GameData.Domains.DomainManager.Information.AddSecretInformationMetaData(GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext, secretInfoOffset, true);
                                            }
                                        }
                                        grave.SetInventory(graveInventory, GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext);
                                    }
                                }

                                if(ResourceCount>0){
                                    var graveResources = grave.GetResources();
                                    if(graveResources.IsNonZero()){
                                        int currDate = GameData.Domains.DomainManager.World.GetCurrDate();
                                        var lifeRecordCollection = GameData.Domains.DomainManager.LifeRecord.GetLifeRecordCollection();
                                        var secretInformationCollection = GameData.Domains.DomainManager.Information.GetSecretInformationCollection();
                                        var location = selfChar.GetLocation();
                                        for(sbyte targetResourceType=0;targetResourceType<8;targetResourceType++){
                                            var deltaAmount=(int)((long)graveResources.Get(targetResourceType)*(long)ResourceCount/10000L);
                                            if(deltaAmount>0){
                                                selfChar.ChangeResource(GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext, targetResourceType, deltaAmount);
                                                lifeRecordCollection.AddRobResourceFromGraveSucceed(selfCharId, currDate, graveId, location, targetResourceType, deltaAmount);
                                                var secretInfoOffset = secretInformationCollection.AddRobGraveResource(selfCharId, graveId, targetResourceType);
                                                GameData.Domains.DomainManager.Information.AddSecretInformationMetaData(GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext, secretInfoOffset, true);
                                            }
                                        }
                                        graveResources=graveResources.Subtract(ref graveResources);
                                        grave.SetResources(ref graveResources, GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // logger("-> "+combatConfigId);
        }
    }
    [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"LogCharacterInteractDate")]
    public class SkipLogCharacterInteractDate {
        public static bool Prefix()=>false;
    }
    [HarmonyPatch(typeof(GameData.Domains.Extra.ExtraDomain),"FindTreasure")]
    public class ExtraDomainFindTreasure {
        public static void Prefix(ref int ____treasureMaterialFailedTimes)=>____treasureMaterialFailedTimes=10000;
    }
    public class EventHelperSelectTaiwuTeammateForMatchmaking {
        [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"SelectTaiwuTeammateForMatchmaking"), HarmonyTranspiler]
        public static IEnumerable<CodeInstruction> Transpiler_1(MethodBase __originalMethod, IEnumerable<CodeInstruction> instructions) {
            if(BetterSkipSkill.Enable("N09-Add"))instructions = instructions.MethodReplacer(typeof(GameData.Domains.Character.Character).GetMethod("GetAgeGroup", (BindingFlags)(-1)), typeof(EventHelperSelectTaiwuTeammateForMatchmaking).GetMethod("GetAgeGroup"));
            instructions = instructions.MethodReplacer(typeof(GameData.Domains.Character.Relation.RelationType).GetMethod("AllowAddingHusbandOrWifeRelation", (BindingFlags)(-1)), typeof(EventHelperSelectTaiwuTeammateForMatchmaking).GetMethod("AllowAddingHusbandOrWifeRelation"));
            instructions = instructions.MethodReplacer(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper).GetMethod("IsOppositeGender", (BindingFlags)(-1)), typeof(EventHelperSelectTaiwuTeammateForMatchmaking).GetMethod("IsOppositeGender"));
            return instructions;
        }
        [HarmonyPatch(typeof(GameData.Domains.Organization.OrganizationDomain),"JoinSpouseOrganization"), HarmonyTranspiler]
        public static IEnumerable<CodeInstruction> Transpiler_2(MethodBase __originalMethod, IEnumerable<CodeInstruction> instructions) {
            instructions = instructions.MethodReplacer(typeof(GameData.Utilities.Tester).GetMethod("Assert", (BindingFlags)(-1)), typeof(EventHelperSelectTaiwuTeammateForMatchmaking).GetMethod("Assert"));
            return instructions;
        }
        public static sbyte GetAgeGroup(GameData.Domains.Character.Character _)=>2;
        public static bool AllowAddingHusbandOrWifeRelation(int ch, int ch2)=>true;
        public static bool IsOppositeGender(GameData.Domains.Character.Character ch, GameData.Domains.Character.Character ch2)=>true;
        public static void Assert(bool truth,string fake){}
    }
    // [HarmonyPatch(typeof(GameData.Domains.TaiwuEvent.EventHelper.EventHelper),"StartLifeSkillCombat")]
    // public class AutoStartLifeSkillCombat {
    //     static MethodInfo ShowingEvent=typeof(GameData.Domains.TaiwuEvent.TaiwuEventDomain).GetProperty("ShowingEvent",(BindingFlags)(-1)).GetSetMethod(true);
    //     static MethodInfo SetEventChanged=typeof(GameData.Domains.TaiwuEvent.TaiwuEventDomain).GetMethod("SetEventChanged",(BindingFlags)(-1));
    //     static MethodInfo CanNextEvent=typeof(GameData.Domains.TaiwuEvent.TaiwuEventDomain).GetMethod("CanNextEvent",(BindingFlags)(-1));
    //     static MethodInfo NextEvent=typeof(GameData.Domains.TaiwuEvent.TaiwuEventDomain).GetMethod("NextEvent",(BindingFlags)(-1));
    //     static MethodInfo SetEventCount=typeof(GameData.Domains.TaiwuEvent.TaiwuEventDomain).GetMethod("SetEventCount",(BindingFlags)(-1));
    //     public static bool Prefix(GameData.Domains.TaiwuEvent.EventArgBox argBox, string onFinishEventId){
    //         GameData.Domains.DomainManager.TaiwuEvent.SetListener(onFinishEventId, argBox);
    //         GameData.Domains.DomainManager.TaiwuEvent.SetListenerBoolArg("WinState", return_status);
    //         GameData.Domains.DomainManager.TaiwuEvent.TriggerListener(null, 0);
    //         //GameData.Domains.DomainManager.TaiwuEvent.ToEvent(onFinishEventId);
    //         // logger("Enter prefix and set value done.");
    //         // GameData.Domains.TaiwuEvent.TaiwuEvent nextEvent = GameData.Domains.DomainManager.TaiwuEvent.GetEvent(onFinishEventId);
    //         // logger("2");
    //         // if (nextEvent != null) {
    //         //     logger("b0");
    //         //     nextEvent.ArgBox = argBox;
    //         //     logger("b1");
    //         //     if (nextEvent.EventConfig.OnCheckEventCondition()) {
    //         //         logger("b20");
    //         //         ShowingEvent.Invoke(GameData.Domains.DomainManager.TaiwuEvent,new object[]{nextEvent});
    //         //         logger("b21");
    //         //         SetEventChanged.Invoke(GameData.Domains.DomainManager.TaiwuEvent,null);
    //         //         logger("b22");
    //         //     } else {
    //         //         if ((bool)(CanNextEvent.Invoke(GameData.Domains.DomainManager.TaiwuEvent,null))) {
    //         //             logger("Bb20");
    //         //             NextEvent.Invoke(GameData.Domains.DomainManager.TaiwuEvent,null);
    //         //             logger("Bb21");
    //         //         } else {
    //         //             logger("BB20");
    //         //             ShowingEvent.Invoke(GameData.Domains.DomainManager.TaiwuEvent,new object[]{GameData.Domains.TaiwuEvent.TaiwuEvent.Empty});
    //         //             logger("BB21");
    //         //             SetEventCount.Invoke(GameData.Domains.DomainManager.TaiwuEvent,new object[]{0, GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext});
    //         //             logger("BB22");
    //         //         }
    //         //     }
    //         //     logger("3");
    //         // } else {
    //         //     GameData.Utilities.AdaptableLog.TagError("BetterSkipSkill(Mod)", "TaiwuEvent Error: can not find event " + onFinishEventId);
    //         // }
    //         return false;
    //     }
    // }
}
